import Watcher from './Watcher'
export default class Compile {
  constructor(el, vue) {
    // vue示例
    this.$vue = vue
    // 挂载点
    this.$el = document.querySelector(el)
    // 如果用户传入了挂载点
    if (this.$el) {
      // 调用函数，让节点变为fragment,类似于mustache中的tokens,
      // vue实际上用的是AST，这里就是轻量级的fragment
      let $fragment = this.node2Fragments(this.$el)

      // 编译
      this.compile($fragment)
      // 替换好的内容要上树
      this.$el.appendChild($fragment)
    }
  }
  // node2Fragments函数的作用是将dom节点转换为虚拟节点
  node2Fragments(el) {
    let fragment = document.createDocumentFragment()
    let child
    // 被fragment.appendChild的真是节点会消失，所以这样每次el.firstChild都会改变，
    // 直到所有dom节点都进入fragment，循环结束
    while ((child = el.firstChild)) {
      fragment.appendChild(child)
    }

    return fragment
  }

  compile(el) {
    // 得到子元素
    let childNodes = el.childNodes
    let self = this
    let reg = /\{\{(.*)\}\}/
    childNodes.forEach((node) => {
      let text = node.textContent
      /*
        nodeType 属性返回节点类型。
        如果节点是一个元素节点，nodeType 属性返回 1。
        如果节点是属性节点, nodeType 属性返回 2。
        如果节点是一个文本节点，nodeType 属性返回 3。
        如果节点是一个注释节点，nodeType 属性返回 8。
       */
      if (node.nodeType === 1) {
        self.compileElement(node)
      } else if (node.nodeType === 3 && reg.test(text)) {
        let name = text.match(reg)[1]
        console.log('文本匹配')
        self.compileText(node, name)
      }
    })
  }
  compileElement(node) {
    let self = this
    let nodeAttrs = node.attributes
    // Array.prototype.slice.call(nodeAttrs)方法将类数组对象变为数组
    Array.prototype.slice.call(nodeAttrs).forEach((attr) => {
      // 这里就分析指令
      let attrName = attr.name
      let value = attr.value
      // 什么指令，截取v-以后的字符串
      let dir = attrName.substring(2)
      // 指令都是v-开头的
      if (attrName.indexOf('v-') == 0) {
        if (dir == 'model') {
          console.log('发现了v-model指令', value)
          new Watcher(self.$vue, value, (val) => {
            node.value = val
          })
          const v = self.getVueVal(self.$vue, value)
          node.value = v
          // 添加监听
          node.addEventListener('input', (e) => {
            const newVal = e.target.value
            self.setVueVal(self.$vue, value, newVal)
          })
        } else if (dir == 'if') {
          console.log('发现了v-if指令', value)
        }
      }
      console.log(dir)
    })
  }

  compileText(node, name) {
    node.textContent = this.getVueVal(this.$vue, name)
    console.log(node.textContent, 666)

    new Watcher(this.$vue, name, (value) => {
      node.textContent = value
    })
  }

  getVueVal(vue, exp) {
    let val = vue
    exp = exp.split('.')
    exp.forEach((k) => {
      val = val[k]
    })

    return val
  }

  setVueVal(vue, exp, value) {
    let val = vue
    exp = exp.split('.')
    exp.forEach((k, i) => {
      if (i < exp.length - 1) {
        val = val[k]
      } else {
        val[k] = value
      }
    })
  }
}
